import time

import allure
from selenium import webdriver
from selenium.common import StaleElementReferenceException, TimeoutException
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.wait import WebDriverWait


class BasePage:
    _BASE_URL = ""

    def __init__(self, base_driver=None):
        # options = webdriver.ChromeOptions()
        # options.add_argument("--start-maximized") # 启动就是最大化
        if base_driver:
            self.driver = base_driver
        else:
            # self.driver = webdriver.Chrome()
            chrome_driver_path = "D:/Google/chromedriver-win64/chromedriver-win64/chromedriver.exe"
            service = Service(chrome_driver_path)
            self.driver = webdriver.Chrome(service=service)

        self.driver.implicitly_wait(5)
        self.driver.maximize_window()
        self.wait = WebDriverWait(self.driver, 10)  # 等待最多10秒

        if not self.driver.current_url.startswith("http"):
            self.driver.get(self._BASE_URL)


    def do_find_new(self, locator, timeout=10):
        """带等待地查找元素"""
        try:
            element = WebDriverWait(self.driver, timeout).until(
                expected_conditions.visibility_of_element_located(locator)
            )
            return element
        except TimeoutException:
            # 调试：打印所有 el-input__inner
            all_inputs = self.driver.find_elements(By.XPATH, "//*[@class='el-input__inner']")
            print(f"总共找到 {len(all_inputs)} 个 el-input__inner")

            for idx, ele in enumerate(all_inputs):
                try:
                    print(f"索引 {idx + 1}: tag={ele.tag_name}, type={ele.get_attribute('type')}, "
                          f"placeholder={ele.get_attribute('placeholder')}, "
                          f"displayed={ele.is_displayed()}")
                except Exception as e:
                    print(f"索引 {idx + 1}: 无法获取信息 ({e})")

            raise Exception(f"超时：未能找到元素 {locator}")


    # @ui_exception_record
    def do_find(self, by, locator=None):
        """获取单个元素"""
        if locator:
            return self.driver.find_element(by, locator)
        else:
            return self.driver.find_element(*by)

    def do_finds(self, by, locator=None):
        """获取多个元素"""
        if locator:
            return self.driver.find_elements(by, locator)
        else:
            return self.driver.find_elements(*by)

    # def do_send_keys(self, value, by, locator=None):
    #     element = self.do_find(by, locator)
    #     element.clear()
    #     element.send_keys(value)

    def do_send_keys(self, text, locator, max_retries=3):
        """安全地输入文本，自动重试处理 stale 元素"""
        for i in range(max_retries):
            try:
                # element = self.do_find(locator)  # 每次都重新查找
                element = self.do_find_new(locator)  # 每次都重新查找
                element.clear()
                element.send_keys(text)
                return  # 成功则退出
            except StaleElementReferenceException:
                print(f"[do_send_keys] 元素过期，第 {i + 1} 次重试...")
                time.sleep(0.5)
                continue
        raise Exception(f"输入失败：元素持续过期，locator={locator}")

    def do_quit(self):
        self.driver.quit()

    def web_locate_presence(self, by):
        return WebDriverWait(self.driver, 10).until(expected_conditions.presence_of_element_located(by))

    def web_locate_click(self, by):
        return WebDriverWait(self.driver, 10).until(expected_conditions.element_to_be_clickable(by))

    def get_screen(self, name=None):
        if name:
            image_path = f"./images/image_{name}.PNG"
        else:
            timestamp = int(time.time())
            # 一定要提前创建好images 路径
            image_path = f"./images/image_{timestamp}.PNG"
        # 截图
        self.driver.save_screenshot(image_path)
        allure.attach.file(image_path, name="picture",
                           attachment_type=allure.attachment_type.PNG)

    def get_page_source(self):
        timestamp = int(time.time())
        page_source_path = f"./page_source/page_source_{timestamp}.html"
        with open(page_source_path, "w", encoding="utf-8") as f:
            f.write(self.driver.page_source)

        # 将page_source放到报告数据中
        allure.attach.file(page_source_path, name="page_source",
                           attachment_type=allure.attachment_type.TEXT)

    def safe_click(self, element_locator, max_retries=3):
        for i in range(max_retries):
            try:
                element = self.do_find(*element_locator)
                element.click()
                return True
            except StaleElementReferenceException:
                if i == max_retries - 1:
                    raise
                time.sleep(0.5)
        return False

    def click_with_js(self, locator):
        """使用 JS 点击元素，locator 是元组形式的定位器"""
        element = self.do_find(locator)
        self.driver.execute_script("arguments[0].click();", element)

    def scroll_to_element(self, locator):
        """将元素滚动到视口中心，locator 是元组形式的定位器"""
        element = self.do_find(locator)  # 在方法内部查找
        self.driver.execute_script(
            "arguments[0].scrollIntoView({behavior: 'smooth', block: 'center', inline: 'center'});",
            element
        )
        time.sleep(0.3)

    def get_names(self, element, min_text_length=1):
        try:
            # 等待元素出现
            self.wait.until(expected_conditions.presence_of_all_elements_located(element))

            # 等待至少一个元素有文本
            self.wait.until(
                lambda d: any(
                    len(elem.text.strip()) >= min_text_length
                    for elem in d.find_elements(*element)
                )
            )

            # 获取所有文本
            elements = self.driver.find_elements(*element)
            return [elem.text.strip() for elem in elements]

        except TimeoutException:
            timestamp = int(time.time())
            self.driver.save_screenshot(f"./images/timeout_get_names_{timestamp}.png")
            print(f"[WARNING] 获取元素超时: {element}")
            return []
